Skip to content

Fix issue #22 - #23

Merged
lohedges merged 20 commits into
develfrom
fix_22
Nov 12, 2026
Merged

lohedges merged 20 commits into
develfrom
fix_22

Conversation

@lohedges

@lohedges lohedges commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Closes #22.

Ghostly typed bridge atoms with RDKit on end-state molecules that still had their ghost atoms attached. When the extra bonds to ghosts exceeded the valence of a bridge atom, bond-order inference failed silently and every heavy atom came back sp3 and non-aromatic, so aromatic triple junctions were treated as non-planar and their ghost angles were softened. Fixing this showed that hybridisation is a poor proxy for the geometric questions Ghostly actually asks, so these decisions now come from the force field terms that are simulated.

Changes

  • RDKit typing removed: bridge typing no longer uses RDKit hybridisation. The only remaining use of RDKit is the chirality check in _linearise_ring_break (off by default), which now converts the ghost-free end state. If bond orders can't be inferred, ring-break ghosts that could be stereocentres (four or more neighbours where they're physical) aren't linearised, and a warning is logged.
  • Triple-junction planarity: decided from the energy needed to flatten the angle terms between the physical neighbours of the bridge, shortfall^2 / sum(1/k), with a 2 kcal/mol cutoff. Bridges with four or more physical neighbours are never planar. If an angle term is missing, ring bridges keep their angles and other bridges are treated as non-planar. Across the OpenFE benchmark set the triple-junction values split cleanly: 326 bridges at ≤ 1.2 kcal/mol and 8 at ≥ 3.3 kcal/mol.
  • Ring membership: for stiffening and keeping impropers, taken from the physical atoms of the end state, so rings closed only by ghosts don't count. A bond is in a ring if its atoms stay connected once it is removed, so rings of any size are found, whereas Sire's in_ring only finds rings of up to 12 atoms. The rotatable-bond check keeps bonds to ghosts, since these restrain rotation at that end state.
  • Ring bridges keep their ghost impropers: impropers centred on a physical ring atom with a single ghost are no longer removed. Without them, the angle terms don't hold the ghost in the plane and it crosses freely. The rigid ring keeps the coupling acceptable (Boresch et al.). --stiffen-ring-bridges is removed.
  • All non-ring bridges are stiffened: the sp2 skip is removed. With the impropers removed, the angle terms of an sp2 bridge don't hold the ghost in the plane either. --stiffen-sp2-bridges is removed.
  • Rotatable bonds: detected from the angle terms. A bridge is a rotor unless it is planar, by the same flattening energy and cutoff, or linear.
  • Tidy up: the end-state connectivities, with and without bonds to ghosts, are built once in modify() and passed through. Impropers are removed once per end state, before the bridge handlers, rather than once per bridge.

Breaking changes

  • The --stiffen-ring-bridges / stiffen_ring_bridges and --stiffen-sp2-bridges / stiffen_sp2_bridges options are removed. Both defaulted to False.
  • The modifications change for some perturbations. See below.

Validation

Scan of the OpenFE benchmark set (604 RBFE edges), default options, devel vs this branch: 212 edges change and 392 are identical, with no errors on either branch. An edge can have more than one cause.

  • Ring-bridge impropers kept: 157 edges
  • Perception / triple-junction planarity: 74 edges
  • Newly stiffened acyclic bridges: 13 edges
  • Bridges in rings closed only by ghosts, now stiffened: 3 edges

Ghost out-of-plane angle over 2 ns at the end state (free leg). Crossings count moves from beyond +20° to beyond −20°.

Bridge Ring Flattening energy (kcal/mol) Old: within 20° of plane Old: crossings New: within 20° of plane New: crossings
galectin 18e~18h O51 no 0.25 54% 478 0% (~90°) 0
cdk2 34~25 O24 no 0.19 50% 487 0% (~55°) 0
ptp1b 23467~23466 O20 no 0.81 43% 577 0% (~58°) 0
galectin 18a~18d O51 no 2.17 5% 567 0% (~86°) 0
bace_baumann lig_41~lig_81 N19 yes n/a 42% 580 94% 67

"Old" is without stiffening for acyclic bridges, or with the impropers removed for the ring bridge. "New" is this branch. All runs were stable.

Tests:

  • New unit tests for _flattening_energy, _is_rotor, _in_ring (including a 14-membered ring that Sire's in_ring misses), _physical_connectivity (including a ring closed only by ghosts), _to_rdkit_end_state, and the chirality fallback in _linearise_ring_break.
  • test_toluene_to_pyridine checks that the ring-bridge improper is kept.
  • test_ejm49_to_ejm31 checks that the sp2 bridge is now stiffened.
  • test_acetone_to_propenol no longer needs the removed option.

A full re-preparation and equilibration s branch is in progress.

Also closes #24. At a terminal junction, Ghostly keeps one anchor dihedral per ghost, ghost–bridge–P1–P2. When the bridge's physical neighbour is sp hybridised at that end state, e.g. a ghost group attached to a nitrile nitrogen, the angle bridge–P1–P2 is linear, so the kept dihedrals pass through three collinear atoms, where they are undefined and their forces diverge at the end state itself. This was found in edges from the OpenFE failure-cases repository (asap eva71_2apro_first_test, EV712AproBLRN, SHP2XPMN and pxr), where the kept anchor dihedrals had force constants of up to about 2.9 kcal/mol. Dihedrals and impropers involving ghost atoms that pass through an angle that is linear at the end state (an equilibrium value of at least 170°, matching SOMD2's torsion_window detection) are now removed, as force fields do for torsions through linear groups. The ghosts remain held by their angle terms, so they can only rotate about the linear axis. Anchor selection also now prefers a candidate that isn't reached through a linear angle, when there is one. Across the 604 OpenFE benchmark edges, this removes dihedrals in 23 edges, while the change to anchor selection affects none.

@lohedges lohedges added the bug Something isn't working label Nov 10, 2026
@lohedges
lohedges deployed to ghostly-build November 10, 2026 20:21 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 10, 2026 20:21 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 10, 2026 20:21 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 10, 2026 20:21 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 08:37 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 08:37 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 08:37 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 08:37 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:28 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:28 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:28 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:28 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:36 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:36 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:36 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 09:36 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:17 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:17 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:17 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:17 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:24 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:24 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:24 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:24 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:28 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:28 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:28 — with GitHub Actions Active
@lohedges
lohedges deployed to ghostly-build November 11, 2026 14:28 — with GitHub Actions Active
@lohedges
lohedges merged commit 95e8480 into devel Nov 12, 2026
4 checks passed
@lohedges
lohedges deleted the fix_22 branch November 12, 2026 12:20

This branch was successfully deployed

1 active deployment
ghostly-build — ae91e522 Deployed Nov 11, 2026 by lohedges via build (3.14, macos) #95
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Anchor dihedrals kept through linear angles at terminal junctions RDKit perception of end states is wrong when ghost atoms are present

1 participant